#!/usr/bin/evn python
# coding=utf-8
# + + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + +
#        ┏┓　　　┏┓+ +
# 　　　┏┛┻━━━┛┻┓ + +
# 　　　┃　　　　　　 ┃ 　
# 　　　┃　　　━　　　┃ ++ + + +
# 　　 ████━████ ┃+
# 　　　┃　　　　　　 ┃ +
# 　　　┃　　　┻　　　┃
# 　　　┃　　　　　　 ┃ + +
# 　　　┗━┓　　　┏━┛
# 　　　　　┃　　　┃　　　　　　　　　　　
# 　　　　　┃　　　┃ + + + +
# 　　　　　┃　　　┃　　　　Codes are far away from bugs with the animal protecting　　　
# 　　　　　┃　　　┃ + 　　　　神兽保佑,代码无bug　　
# 　　　　　┃　　　┃
# 　　　　　┃　　　┃　　+　　　　　　　　　
# 　　　　　┃　 　　┗━━━┓ + +
# 　　　　　┃ 　　　　　　　┣┓
# 　　　　　┃ 　　　　　　　┏┛
# 　　　　　┗┓┓┏━┳┓┏┛ + + + +
# 　　　　　　┃┫┫　┃┫┫
# 　　　　　　┗┻┛　┗┻┛+ + + +
# + + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + +"""
"""
# @Time  : 2019/10/22 15:12

# @Author : mayn

# @Project : zflaskframe

# @FileName: dict_helper.py

# @Software: PyCharm

# 作者：小钟同学

# 著作权归作者所有。

# 文件功能描述: 
"""


# 合并多个dict
def merge_dicts(*dict_args):
    """
    Given any number of dicts, shallow copy and merge into a new dict,
    precedence goes to key value pairs in latter dicts.
    """
    result = {}
    for dictionary in dict_args:
        result.update(dictionary)
    return result


def merge_two(dict1, dict2):
    res = {**dict1, **dict2}
    return res


class AttrDict(dict):
    """
    继承自dict，实现可以通过.来操作元素
    Examples:
        >>> attr_dict = AttrDict(data=56999, step=0, s_status='process', status=0)
        >>> print(attr_dict.data)
    """

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __getattr__(self, item):
        try:
            return self.__getitem__(item)
        except KeyError:
            raise AttributeError(item)

    def __delattr__(self, item):
        self.__delitem__(item)


# ============================
from typing import Awaitable, Callable, TypeVar

T = TypeVar("T")
KT = TypeVar("KT")
VT = TypeVar("VT")
from typing import Dict, Optional
import copy
import hashlib
import pickle


# ############################################################################
# ############################# COPY From 开源项目：emmett-framework #############################
# ############ 开源地址： https://github.com/emmett-framework/emmett #####################
# ############################################################################

class AttdictV2(Dict[KT, VT]):
    # 继承自dict，实现可以通过.来操作元素
    __slots__ = ()

    __setattr__ = dict.__setitem__  # type: ignore
    __delattr__ = dict.__delitem__  # type: ignore
    __getitem__ = dict.get  # type: ignore

    # see http://stackoverflow.com/questions/10364332/how-to-pickle-python-object-derived-from-dict
    def __getattr__(self, key: str) -> Optional[VT]:
        if key.startswith('__'):
            raise AttributeError
        return self.get(key, None)  # type: ignore

    __repr__ = lambda self: '<sdict %s>' % dict.__repr__(self)
    __getstate__ = lambda self: None
    __copy__ = lambda self: AttdictV2(self)
    __deepcopy__ = lambda self, memo: AttdictV2(copy.deepcopy(dict(self)))


class ConfigData(AttdictV2[KT, VT]):
    __slots__ = ()

    def __getitem__(self, key):
        if key not in self.keys():
            self[key] = AttdictV2()
        return super().__getitem__(key)

    __getattr__ = __getitem__


class SessionData(AttdictV2):
    __slots__ = ('__sid', '__hash', '__expires', '__dump')

    def __init__(self, initial=None, sid=None, expires=None):
        AttdictV2.__init__(self, initial or ())
        object.__setattr__(
            self, '_SessionData__dump', pickle.dumps(AttdictV2(self)))
        h = hashlib.md5(self._dump).hexdigest()
        object.__setattr__(self, '_SessionData__sid', sid)
        object.__setattr__(self, '_SessionData__hash', h)
        object.__setattr__(self, '_SessionData__expires', expires)

    @property
    def _sid(self):
        return self.__sid

    @property
    def _modified(self):
        dump = pickle.dumps(AttdictV2(self))
        h = hashlib.md5(dump).hexdigest()
        if h != self.__hash:
            object.__setattr__(self, '_SessionData__dump', dump)
            return True
        return False

    @property
    def _expiration(self):
        return self.__expires

    @property
    def _dump(self):
        # note: self.__dump is updated only on _modified call
        return self.__dump

    def _expires_after(self, value):
        object.__setattr__(self, '_SessionData__expires', value)
